package com.douyin.aspect;

import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.util.StopWatch;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 请求日志切面
 * 用于记录所有HTTP请求的输入输出和耗时信息
 */
@Slf4j
@Aspect
@Component
public class RequestLogAspect {

  /**
   * 定义切点 - 所有controller包下的所有方法
   */
  @Pointcut("execution(* com.douyin.controller.*.*(..))")
  public void requestLog() {
  }

  /**
   * 环绕通知
   *
   * @param proceedingJoinPoint 连接点
   * @return 返回值
   * @throws Throwable 异常
   */
  @Around("requestLog()")
  public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();

    // 获取请求相关信息
    ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
    HttpServletRequest request = attributes != null ? attributes.getRequest() : null;

    // 获取请求方法名
    String methodName = proceedingJoinPoint.getSignature().getName();
    String className = proceedingJoinPoint.getTarget().getClass().getSimpleName();

    // 获取方法参数名和参数值
    MethodSignature methodSignature = (MethodSignature) proceedingJoinPoint.getSignature();
    String[] paramNames = methodSignature.getParameterNames();
    Object[] args = proceedingJoinPoint.getArgs();

    // 构建参数Map
    Map<String, Object> params = null;
    if (paramNames != null && paramNames.length > 0) {
      params = new HashMap<String, Object>();
      for (int i = 0; i < paramNames.length; i++) {
        String param = paramNames[i];
        Object value = i < args.length ? args[i] : null;
        params.put(param, value);
      }
    }

    // 获取请求URL和方法
    String url = request != null ? request.getRequestURL().toString() : "";
    String method = request != null ? request.getMethod() : "";

    // 打印请求日志
    log.info("请求开始 - URL: {}, 方法: {}, Controller: {}.{}",
        url, method, className, methodName);

    // 执行目标方法
    Object result = null;
    try {
      result = proceedingJoinPoint.proceed();
      return result;
    } finally {
      // 记录请求耗时
      stopWatch.stop();
      long totalTimeMillis = stopWatch.getTotalTimeMillis();
      log.info("请求结束 - URL: {}, 方法: {}, Controller: {}.{}, 响应: {}, 耗时: {}毫秒",
          url, method, className, methodName,
          result != null ? JSON.toJSONString(result) : "无返回值",
          totalTimeMillis);
    }
  }
}